﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate.Mapping.ByCode;
using NHibernate.Cfg;
using System.Reflection;

namespace BeardCoder.Data.Framework.NHibernate
{
    public static class Conventions
    {
        public static void ApplyConventions(this ConventionModelMapper mapper, Configuration configuration)
        {
            mapper.IsEntity((type, declared) => type.IsEntity());
            mapper.IsRootEntity((type, declared) => type.IsEntity());

            mapper.BeforeMapClass += (modelInspector, type, classCustomizer) =>
            {
                classCustomizer.Id(c => c.Column("Id"));
                classCustomizer.Id(c => c.Generator(Generators.Identity));
                classCustomizer.Table(Inflector.Inflector.Pluralize(type.Name.ToString()));
            };

            mapper.BeforeMapManyToOne += (modelInspector, propertyPath, map) =>
            {
                map.Column(propertyPath.LocalMember.GetPropertyOrFieldType().Name + "Id");
                map.Cascade(Cascade.Persist);
            };

            mapper.BeforeMapBag += (modelInspector, propertyPath, map) =>
            {
                map.Key(keyMapper => keyMapper.Column(propertyPath.GetContainerEntity(modelInspector).Name + "Id"));
                map.Cascade(Cascade.All);
            };
        }

        public static void OverrideConventions(this ConventionModelMapper mapper, Assembly assembly)
        {
            if (assembly == null)
                return;
            var overrideType = typeof(IOverride);

            var types = assembly.GetTypes()
                 .Where(t => overrideType.IsAssignableFrom(t) && t != typeof(IOverride))
                 .ToList();

            types.ForEach(t =>
            {
                var conventionOverride = Activator.CreateInstance(t) as IOverride;
                conventionOverride.Override(mapper);
            });
        }

        public static bool IsEntity(this Type type)
        {
            var genereicEntity = type.BaseType != null && !type.IsInterface && type.BaseType.IsGenericType
            && (type.BaseType.GetGenericTypeDefinition() == typeof(Entity<>));
            var entity = typeof(Entity).IsAssignableFrom(type) && typeof(Entity) != type && !type.IsInterface;

            return entity || genereicEntity;
        }
    }
}
